package com.tozed.test;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
import android.util.AttributeSet;
import android.util.Log;

/**
 * ViewTouchMultiple for touchpanel
*/
public class ViewTouchMultiple extends ViewTouch {

    /**
    * TAG
    */
    private static final String TAG = "ViewTouchMultiple";
    
    /**
    * Circle radius
    */
    private static final float RADIUS = 60.0f;
    
    /**
    * Circle distance
    */
    private static final float DISTANCE = 90.0f;
    
    /**
    * Painter
    */
	private Paint mPaint = null;
    
    /**
    * Screen width and height
    */
    private int mWidth = 0;
    private int mHeight = 0;

    /**
    * Circle 1 center coordination
    */
    private float mCircleCenter1X = 0.0f;
    private float mCircleCenter1Y = 0.0f;

    /**
    * Circle 2 center coordination
    */
    private float mCircleCenter2X = 0.0f;
    private float mCircleCenter2Y = 0.0f;

    private boolean mIsGoon = false;

    private boolean mIsCircle1Touched = false;
    private boolean mIsCircle2Touched = false;

    /**
     * Constructor.
     *
     * @param context the Context, that this is running within
     */
    public ViewTouchMultiple(Context context) {
        super(context);
        init();
    }

    /**
     * Constructor.
     *
     * @param context the Context, that this is running within
     * @param attrs The attributes of the XML tag that is inflating the view.
     */
    public ViewTouchMultiple(Context context, AttributeSet attrs) {
   	    super(context, attrs);
        init();
   	}

    /**
     * Constructor.
     *
     * @param context the Context, that this is running within
     * @param attrs The attributes of the XML tag that is inflating the view.
     * @param defStyle The default style to apply to this view. If 0, no style
     *        will be applied (beyond what is included in the theme). This may
     *        either be an attribute resource, whose value will be retrieved
     *        from the current theme, or an explicit style resource.
     */
    public ViewTouchMultiple(Context context, AttributeSet attrs, int defStyle) {
   	    super(context, attrs, defStyle);
        init();
    }

    /**
     * This is called during layout when the size of this view has changed. If
     * you were just added to the view hierarchy, you're called with the old
     * values of 0.
     *
     * @param w Current width of this view.
     * @param h Current height of this view.
     * @param oldw Old width of this view.
     * @param oldh Old height of this view.
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    	Log.v(TAG, "onSizeChanged w = " + w + " h = " + h + " oldw = " + oldw + " oldh = " + oldh);
        super.onSizeChanged(w, h, oldw, oldh);

        mWidth = (int)w;
        mHeight = (int)h;
    	Log.v(TAG, "onSizeChanged mWidth = " + mWidth + " mHeight = " + mHeight);
        reset();
    }
    
    /**
     * Implement this to do your drawing.
     *
     * @param canvas the canvas on which the background will be drawn
     */
    @Override
    protected void onDraw(Canvas canvas) {
        Log.v(TAG, "onDraw");
        canvas.drawCircle(mCircleCenter1X, mCircleCenter1Y, RADIUS, mPaint);
        canvas.drawCircle(mCircleCenter2X, mCircleCenter2Y, RADIUS, mPaint);
    }

    /**
     * Implement this method to handle touch screen motion events.
     *
     * @param event The motion event.
     * @return True if the event was handled, false otherwise.
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int NP = event.getPointerCount();
    	Log.v(TAG, "onTouchEvent NP = " + NP);
        if (NP == 1) {
            float x = event.getX();
            float y = event.getY();
            Log.v(TAG, "onTouchEvent x = " + x + " y = " + y);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Log.v(TAG, "onTouchEvent ACTION_DOWN");
                    onActionDown(x, y);
                    return true;
                case MotionEvent.ACTION_MOVE:
                    Log.v(TAG, "onTouchEvent 1 ACTION_MOVE");
                    onActionMove(x, y);
                    return true;
                case MotionEvent.ACTION_UP:
                    Log.v(TAG, "onTouchEvent ACTION_UP");
                    onActionUp(x, y);
                    return true;
                default:
                    break;
            }
        } else if (NP >= 2) {
            float x0 = event.getX(0);
            float y0 = event.getY(0);
            float x1 = event.getX(1);
            float y1 = event.getY(1);
            switch (event.getAction()) {
                case MotionEvent.ACTION_POINTER_2_DOWN:
                    Log.v(TAG, "onTouchEvent ACTION_POINTER_2_DOWN");
                    onActionDown2(x0, y0, x1, y1);
                    return true;
                case MotionEvent.ACTION_MOVE:
                    Log.v(TAG, "onTouchEvent 2 ACTION_MOVE");
                    onActionMove2(x0, y0, x1, y1);
                    return true;
                case MotionEvent.ACTION_POINTER_UP:
                    Log.v(TAG, "onTouchEvent ACTION_POINTER_UP");
                    onActionUp2(x0, y0, x1, y1);
                    return true;
                default:
                    break;
            }
        }


        return super.onTouchEvent(event);
    }

    /**
     * Utility 
     * For initialization
     */
    private void init() {
		mPaint = new Paint();
        mPaint.setAntiAlias(true);
		mPaint.setColor(Color.RED);
		mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		mPaint.setStrokeCap(Paint.Cap.ROUND);
		mPaint.setStrokeWidth(2);
    }

    /**
     * Utility 
     * For reset
     */
    private void reset() {
		mPaint.setColor(Color.RED);
        mCircleCenter1X = RADIUS + 45;
        mCircleCenter1Y = RADIUS + 45;
        mCircleCenter2X = mWidth - RADIUS - 65;
        mCircleCenter2Y = mHeight - RADIUS - 65;
    }

    /**
     * Utility 
     * If the point(x, y) is in circle(centerX, centerY) 
     *
     * @param x The touch X ordination.
     * @param y The touch Y ordination.
     * @param centerX The circle center X ordination.
     * @param centerY The circle center Y ordination.
     * @return boolean If the point(x, y) is in circle(centerX, centerY).
     */
    private boolean isInCircle(float x, float y, float centerX, float centerY) {
        //Log.v(TAG, "isInCircle x = " + x + " y = " + y + " centerX = " + centerX + " centerY = " + centerY);
        double radius = distance(x, y, centerX, centerY);
        //Log.v(TAG, "isInCircle radius = " + radius);

        if (Double.compare(radius, (double)RADIUS) <= 0) {
            return true;
        }

        return false;
    }

    /**
     * Utility 
     * get the distance between point0(x0, y0) and point1(x1, y1) 
     *
     * @param x0 The point0 X ordination.
     * @param y0 The point0 Y ordination.
     * @param x1 The point1 X ordination.
     * @param y1 The point1 Y ordination.
     * @return double The distance between point0(x0, y0) and point1(x1, y1).
     */
    private double distance(float x0, float y0, float x1, float y1) {
        //Log.v(TAG, "distance x = " + x + " y = " + y + " centerX = " + centerX + " centerY = " + centerY);
        float vx = Math.abs(x0 - x1);
        float vy = Math.abs(y0 - y1);

        double distance = Math.sqrt(vx * vx + vy * vy);
        //Log.v(TAG, "distance distance = " + distance);

        return distance;
    }

    /**
     * Utility 
     * Revise X value 
     *
     * @param x The original X value
     * @return float The revised X value.
     */
    private float reviseX(float x) {
        if (x < RADIUS) {
            x = RADIUS;
        } else if (x > (mWidth - RADIUS)) {
            x = (mWidth - RADIUS);
        }

        return x;
    }

    /**
     * Utility 
     * Revise Y value 
     *
     * @param y The original Y value
     * @return float The revised Y value.
     */
    private float reviseY(float y) {
        if (y < RADIUS) {
            y = RADIUS;
        } else if (y > (mHeight - RADIUS)) {
            y = (mHeight - RADIUS);
        }

        return y;
    }

    /**
     * Utility 
     * Handle  MotionEvent.ACTION_DOWN
     *
     * @param x0 The touch X ordination.
     * @param y0 The touch Y ordination.
     */
	private void onActionDown(float x0, float y0) {
        Log.v(TAG, "onActionDown x0 = " + x0 + " y0 = " + y0);
	}

    /**
     * Utility 
     * Handle  MotionEvent.ACTION_MOVE
     *
     * @param x0 The touch X ordination.
     * @param y0 The touch Y ordination.
     */
	private void onActionMove(float x0, float y0) {
        Log.v(TAG, "onActionMove x0 = " + x0 + " y0 = " + y0);
	}

    /**
     * Utility 
     * Handle  MotionEvent.ACTION_UP
     *
     * @param x0 The touch X ordination.
     * @param y0 The touch Y ordination.
     */
	private void onActionUp(float x0, float y0) {
        Log.v(TAG, "onActionUp x0 = " + x0 + " y0 = " + y0);
        if (mIsGoon) {
            if (isInCircle(x0, y0, mCircleCenter1X, mCircleCenter1Y)) {
                mIsCircle1Touched = false;
                Log.v(TAG, "onActionUp x0y0 set mIsCircle1Touched false");
            } else if (isInCircle(x0, y0, mCircleCenter2X, mCircleCenter2Y)) {
                mIsCircle2Touched = false;
                Log.v(TAG, "onActionUp x0y0 set mIsCircle2Touched false");
            }
            
            Log.v(TAG, "onActionUp mIsCircle1Touched = " + mIsCircle1Touched + " mIsCircle2Touched = " + mIsCircle2Touched);
            mIsGoon = false;
            
            //reset();
            
            invalidate();
        }
	}

    /**
     * Utility 
     * Handle  MotionEvent.ACTION_DOWN
     *
     * @param x0 The touch0 X ordination.
     * @param y0 The touch0 Y ordination.
     * @param x1 The touch1 X ordination.
     * @param y1 The touch1 Y ordination.
     */
	private void onActionDown2(float x0, float y0, float x1, float y1) {
        Log.v(TAG, "onActionDown2 x0 = " + x0 + " y0 = " + y0 + " x1 = " + x1 + " y1 = " + y1
            + " mCircleCenter1X = " + mCircleCenter1X + " mCircleCenter1Y = " + mCircleCenter1Y
            + " mCircleCenter2X = " + mCircleCenter2X + " mCircleCenter2Y = " + mCircleCenter2Y);

        if (isInCircle(x0, y0, mCircleCenter1X, mCircleCenter1Y)) {
            mIsCircle1Touched = true;
            Log.v(TAG, "onActionDown2 x0y0 set mIsCircle1Touched true");
        } else if (isInCircle(x0, y0, mCircleCenter2X, mCircleCenter2Y)) {
            mIsCircle2Touched = true;
            Log.v(TAG, "onActionDown2 x0y0 set mIsCircle2Touched true");
        }

        if (isInCircle(x1, y1, mCircleCenter1X, mCircleCenter1Y)) {
            mIsCircle1Touched = true;
            Log.v(TAG, "onActionDown2 x1y1 set mIsCircle1Touched true");
        } else if (isInCircle(x1, y1, mCircleCenter2X, mCircleCenter2Y)) {
            mIsCircle2Touched = true;
            Log.v(TAG, "onActionDown2 x1y1 set mIsCircle2Touched true");
        }

        Log.v(TAG, "onActionDown2 mIsCircle1Touched = " + mIsCircle1Touched + " mIsCircle2Touched = " + mIsCircle2Touched);
        if (mIsCircle1Touched && mIsCircle2Touched) {
            mIsGoon = true;
        }
	}

    /**
     * Utility 
     * Handle  MotionEvent.ACTION_MOVE
     *
     * @param x0 The touch0 X ordination.
     * @param y0 The touch0 Y ordination.
     * @param x1 The touch1 X ordination.
     * @param y1 The touch1 Y ordination.
     */
	private void onActionMove2(float x0, float y0, float x1, float y1) {
        Log.v(TAG, "onActionMove2 x0 = " + x0 + " y0 = " + y0 + " x1 = " + x1 + " y1 = " + y1
            + " mCircleCenter1X = " + mCircleCenter1X + " mCircleCenter1Y = " + mCircleCenter1Y
            + " mCircleCenter2X = " + mCircleCenter2X + " mCircleCenter2Y = " + mCircleCenter2Y);
        if (mIsGoon) {
            double distance = distance(mCircleCenter1X, mCircleCenter1Y, mCircleCenter2X, mCircleCenter2Y);
            Log.v(TAG, "onActionMove2 begin distance = " + distance);
            if (Double.compare(distance, (double)(2 * RADIUS + DISTANCE)) <= 0) {
                float x = ((2 * RADIUS + DISTANCE) - (float)distance) * (mCircleCenter2X - mCircleCenter1X) / (2 * (float)distance);
                float y = ((2 * RADIUS + DISTANCE) - (float)distance) * (mCircleCenter2Y - mCircleCenter1Y) / (2 * (float)distance);
                Log.v(TAG, "onActionMove2 x = " + x + " y = " + y);

                mCircleCenter1X -= x;
                mCircleCenter1Y -= y;
                Log.v(TAG, "onActionMove2 mCircleCenter1X = " + mCircleCenter1X + " mCircleCenter1Y = " + mCircleCenter1Y);
                mCircleCenter2X += x;
                mCircleCenter2Y += y;
                Log.v(TAG, "onActionMove2 mCircleCenter2X = " + mCircleCenter2X + " mCircleCenter2Y = " + mCircleCenter2Y);
                distance = distance(mCircleCenter1X, mCircleCenter1Y, mCircleCenter2X, mCircleCenter2Y);
                Log.v(TAG, "onActionMove2 end distance = " + distance);
                
                mIsGoon = false;

                mPaint.setColor(Color.BLUE);
                
                onCompletedListener listener = getOnCompletedListener();
                if (listener != null) {
                    listener.onCompleted(this);
                }
            } else {
                if (isInCircle(x0, y0, mCircleCenter1X, mCircleCenter1Y)) {
                    mCircleCenter1X = x0;
                    mCircleCenter1Y = y0;
                    Log.v(TAG, "onActionMove2 x0y0 set center1 coord");
                } else if (isInCircle(x0, y0, mCircleCenter2X, mCircleCenter2Y)) {
                    mCircleCenter2X = x0;
                    mCircleCenter2Y = y0;
                    Log.v(TAG, "onActionMove2 x0y0 set center2 coord");
                }
                
                if (isInCircle(x1, y1, mCircleCenter1X, mCircleCenter1Y)) {
                    mCircleCenter1X = x1;
                    mCircleCenter1Y = y1;
                    Log.v(TAG, "onActionMove2 x1y1 set center1 coord");
                } else if (isInCircle(x1, y1, mCircleCenter2X, mCircleCenter2Y)) {
                    mCircleCenter2X = x1;
                    mCircleCenter2Y = y1;
                    Log.v(TAG, "onActionMove2 x1y1 set center2 coord");
                }

                mCircleCenter1X = reviseX(mCircleCenter1X);
                mCircleCenter1Y = reviseY(mCircleCenter1Y);
                mCircleCenter2X = reviseX(mCircleCenter2X);
                mCircleCenter2Y = reviseY(mCircleCenter2Y);
            }
            invalidate();
        }
	}

    /**
     * Utility 
     * Handle  MotionEvent.ACTION_UP
     *
     * @param x0 The touch0 X ordination.
     * @param y0 The touch0 Y ordination.
     * @param x1 The touch1 X ordination.
     * @param y1 The touch1 Y ordination.
     */
	private void onActionUp2(float x0, float y0, float x1, float y1) {
        Log.v(TAG, "onActionUp2 x0 = " + x0 + " y0 = " + y0 + " x1 = " + x1 + " y1 = " + y1
            + " mCircleCenter1X = " + mCircleCenter1X + " mCircleCenter1Y = " + mCircleCenter1Y
            + " mCircleCenter2X = " + mCircleCenter2X + " mCircleCenter2Y = " + mCircleCenter2Y);
        if (mIsGoon) {
            double distance = distance(mCircleCenter1X, mCircleCenter1Y, mCircleCenter2X, mCircleCenter2Y);
            Log.v(TAG, "onActionUp2 begin distance = " + distance);
            if (Double.compare(distance, (double)(2 * RADIUS + DISTANCE)) <= 0) {
                float x = ((2 * RADIUS + DISTANCE) - (float)distance) * (mCircleCenter2X - mCircleCenter1X) / (2 * (float)distance);
                float y = ((2 * RADIUS + DISTANCE) - (float)distance) * (mCircleCenter2Y - mCircleCenter1Y) / (2 * (float)distance);
                Log.v(TAG, "onActionUp2 x = " + x + " y = " + y);

                mCircleCenter1X -= x;
                mCircleCenter1Y -= y;
                Log.v(TAG, "onActionUp2 mCircleCenter1X = " + mCircleCenter1X + " mCircleCenter1Y = " + mCircleCenter1Y);
                mCircleCenter2X += x;
                mCircleCenter2Y += y;
                Log.v(TAG, "onActionUp2 mCircleCenter2X = " + mCircleCenter2X + " mCircleCenter2Y = " + mCircleCenter2Y);
                distance = distance(mCircleCenter1X, mCircleCenter1Y, mCircleCenter2X, mCircleCenter2Y);
                Log.v(TAG, "onActionUp2 end distance = " + distance);

                mPaint.setColor(Color.BLUE);
                
                onCompletedListener listener = getOnCompletedListener();
                if (listener != null) {
                    listener.onCompleted(this);
                }
            } else {
                if (isInCircle(x0, y0, mCircleCenter1X, mCircleCenter1Y)) {
                    mIsCircle1Touched = false;
                    Log.v(TAG, "onActionUp2 x0y0 set mIsCircle1Touched false");
                } else if (isInCircle(x0, y0, mCircleCenter2X, mCircleCenter2Y)) {
                    mIsCircle2Touched = false;
                    Log.v(TAG, "onActionUp2 x0y0 set mIsCircle2Touched false");
                }
                
                if (isInCircle(x1, y1, mCircleCenter1X, mCircleCenter1Y)) {
                    mIsCircle1Touched = false;
                    Log.v(TAG, "onActionUp2 x1y1 set mIsCircle1Touched false");
                } else if (isInCircle(x1, y1, mCircleCenter2X, mCircleCenter2Y)) {
                    mIsCircle2Touched = false;
                    Log.v(TAG, "onActionUp2 x1y1 set mIsCircle2Touched false");
                }
            }
            
            Log.v(TAG, "onActionUp2 mIsCircle1Touched = " + mIsCircle1Touched + " mIsCircle2Touched = " + mIsCircle2Touched);
            mIsGoon = false;
            
            //reset();
            
            invalidate();
        }
	}
}

